package com.erlang4j.internal.process;

import java.util.Set;

import com.ericsson.otp.erlang.OtpErlangPid;
import com.erlang4j.api.Erlang4jMessageLanguage;
import com.erlang4j.api.IBinding;
import com.erlang4j.api.exceptions.Erlang4jCannotChangeStateToIllegalStateException;
import com.erlang4j.api.exceptions.Erlang4jCannotChangeStateTwiceException;
import com.erlang4j.api.process.IProcess;
import com.erlang4j.api.process.IProcessWithState;

/**
 * <p>
 * This is the IProcess passed into a message handler.
 * </p>
 * 
 * <p>
 * Design Notes
 * <ul>
 * <li>.
 * <li>Because it is a separate object we get some nice transactional semantics: change of state (both bindings and the
 * state name) occurs if and only if the message handler completes.
 * </ul>
 * </p>
 * 
 * @author Phil Rice
 * 
 */
public class ProcessWrapper implements IProcessWithState {

	private final IProcess delegate;
	private String newStateName;
	private IBinding newMatchingBinding;
	private final Set<String> legalStateNames;

	public ProcessWrapper(IProcess delegate, Set<String> legalStateNames) {
		this.delegate = delegate;
		this.legalStateNames = legalStateNames;
		this.newMatchingBinding = delegate.matchingBinding();
		this.newStateName = null;
	}

	public IBinding getNewMatchingBinding() {
		return newMatchingBinding;
	}

	public String getNewStateName() {
		return newStateName;
	}

	public void changeState(String newStateName) {
		if (this.newStateName != null)
			throw new Erlang4jCannotChangeStateTwiceException(this.newStateName, newStateName);
		if (legalStateNames.contains(newStateName))
			this.newStateName = newStateName;
		else
			throw new Erlang4jCannotChangeStateToIllegalStateException(newStateName, legalStateNames);
	}

	public void bind(String key, Object object) {
		newMatchingBinding = newMatchingBinding.bindAllowingChange(key, Erlang4jMessageLanguage.make(object));
	}

	// ====================Delegate methods ===========
	public boolean isAlive() {
		return delegate.isAlive();
	}

	public IBinding matchingBinding() {
		return delegate.matchingBinding();
	}

	public OtpErlangPid self() {
		return delegate.self();
	}

	public void send(Object serverNameOrPid, String pattern, Object... parameters) {
		delegate.send(serverNameOrPid, pattern, parameters);
	}

	public void sendRaw(Object serverNameOrPid, Object object) {
		delegate.sendRaw(serverNameOrPid, object);
	}

	public void sendRpc(Object serverNameOrPid, String atom, Object... parameters) {
		delegate.sendRpc(serverNameOrPid, atom, parameters);
	}

	public void sendWithBinding(Object serverNameOrPid, String pattern, IBinding binding) {
		delegate.sendWithBinding(serverNameOrPid, pattern, binding);
	}

	public void terminate() {
		delegate.terminate();
	}

}
